Code coverage report for src/browser/transport.js

Statements: 51.14% (45 / 88)      Branches: 56.6% (30 / 53)      Functions: 33.33% (6 / 18)      Lines: 51.14% (45 / 88)      Ignored: none     

All files » src/browser/ » transport.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205    1 1                                                               6       6       6 6       6 6 6 6         6 6       6   1   5 5 5 4 4 4   4 4 1 1 3 2   1 1     2         1 1                                 5 5 5 5   5 5     1                                                               1                                                                                   4       3       1 1 1     1    
var _ = require('../utility');
var logger = require('./logger');
 
/*
 * accessToken may be embedded in payload but that should not
 *   be assumed
 *
 * options: {
 *   hostname
 *   protocol
 *   path
 *   port
 *   method
 * }
 *
 *  params is an object containing key/value pairs. These
 *    will be appended to the path as 'key=value&key=value'
 *
 * payload is an unserialized object
 */
 
function get(accessToken, options, params, callback, requestFactory) {
  if (!callback || !_.isFunction(callback)) {
    callback = function() {};
  }
  _.addParamsAndAccessTokenToPath(accessToken, options, params);
 
  var method = 'GET';
  var url = _.formatUrl(options);
  _makeRequest(accessToken, url, method, null, callback, requestFactory);
}

function post(accessToken, options, payload, callback, requestFactory) {
  if (!callback || !_.isFunction(callback)) {
    callback = function() {};
  I}

  if (!payload) {
    return callback(new Error('Cannot send empty request'));
  I}

  var stringifyResult = _.stringify(payload);
  if (stringifyResult.error) {
    return callback(stringifyResult.error);
  I}

  var writeData = stringifyResult.value;
  var method = 'POST';
  var url = _.formatUrl(options);
  _makeRequest(accessToken, url, method, writeData, callback, requestFactory);
}
 
function _makeRequest(accessToken, url, method, data, callback, requestFactory) {
  var request;
  if (requestFactory) {
    request = requestFactory();
  E} else {
    request = _createXMLHTTPObject();
  }
  if (!request) {
    // Give up, no way to send requests
    return callback(new Error('No way to send a request'));
  }
  try {
    try {
      var onreadystatechange = function() {
        try {
          if (onreadystatechange && request.readyState === 4) {
            onreadystatechange = undefined;
E
            var parseResponse = _.jsonParse(request.responseText);
            if (_isSuccess(request)) {
              callback(parseResponse.error, parseResponse.value);
              return;
            } else if (_isNormalFailure(request)) {
              if (request.status === 403) {
                // likely caused by using a server access token
                var message = parseResponse.value && parseResponse.value.message;
                logger.error(message);
              }
              // return valid http status codes
              callback(new Error(String(request.status)));
            } else {
              // IE will return a status 12000+ on some sort of connection failure,
              // so we return a blank error
              // http://msdn.microsoft.com/en-us/library/aa383770%28VS.85%29.aspx
              var msg = 'XHR response had no status code (likely connection failure)';
              callback(_newRetriableError(msg));
            }
          }
        } catch (ex) {
          //jquery source mentions firefox may error out while accessing the
          //request members if there is a network error
          //https://github.com/jquery/jquery/blob/a938d7b1282fc0e5c52502c225ae8f0cef219f0a/src/ajax/xhr.js#L111
          var exc;
          if (ex && ex.stack) {
            exc = ex;
          } else {
            exc = new Error(ex);
          }
          callback(exc);
        }
      };
 
      request.open(method, url, true);
      if (request.setRequestHeader) {
        request.setRequestHeader('Content-Type', 'application/json');
      E  request.setRequestHeader('X-Rollbar-Access-Token', accessToken);
      }
      request.onreadystatechange = onreadystatechange;
      request.send(data);
    } catch (e1) {
      // Sending using the normal xmlhttprequest object didn't work, try XDomainRequest
      if (typeof XDomainRequest !== 'undefined') {
 
      I  // Assume we are in a really old browser which has a bunch of limitations:
        // http://blogs.msdn.com/b/ieinternals/archive/2010/05/13/xdomainrequest-restrictions-limitations-and-workarounds.aspx
 
        // Extreme paranoia: if we have XDomainRequest then we have a window, but just in case
        if (!window || !window.location) {
          return callback(new Error('No window available during request, unknown environment'));
        }

        // If the current page is http, try and send over http
        if (window.location.href.substring(0, 5) === 'http:' && url.substring(0, 5) === 'https') {
          url = 'http' + url.substring(5);
        }

        var xdomainrequest = new XDomainRequest();
        xdomainrequest.onprogress = function() {};
        xdomainrequest.ontimeout = function() {
          var msg = 'Request timed out';
          var code = 'ETIMEDOUT';
          callback(_newRetriableError(msg, code));
        };
        xdomainrequest.onerror = function() {
          callback(new Error('Error during request'));
        };
        xdomainrequest.onload = function() {
          var parseResponse = _.jsonParse(xdomainrequest.responseText);
          callback(parseResponse.error, parseResponse.value);
        };
        xdomainrequest.open(method, url, true);
        xdomainrequest.send(data);
      } else {
        callback(new Error('Cannot find a method to transport a request'));
      }
    }
  } catch (e2) {
    callback(e2);
  }
}
 
function _createXMLHTTPObject() {
  /* global ActiveXObject:false */

  var factories = [
    function () {
      return new XMLHttpRequest();
    },
    function () {
      return new ActiveXObject('Msxml2.XMLHTTP');
    },
    function () {
      return new ActiveXObject('Msxml3.XMLHTTP');
    },
    function () {
      return new ActiveXObject('Microsoft.XMLHTTP');
    }
  ];
  var xmlhttp;
  var i;
  var numFactories = factories.length;
  for (i = 0; i < numFactories; i++) {
    /* eslint-disable no-empty */
    try {
      xmlhttp = factories[i]();
      break;
    } catch (e) {
      // pass
    }
    /* eslint-enable no-empty */
  }
  return xmlhttp;
}

function _isSuccess(r) {
  return r && r.status && r.status === 200;
}
 
function _isNormalFailure(r) {
  return r && _.isType(r.status, 'number') && r.status >= 400 && r.status < 600;
}
 
function _newRetriableError(message, code) {
  var err = new Error(message);
  err.code = code || 'ENOTFOUND';
  return err;
}
 
module.exports = {
  get: get,
  post: post
};